package com.klun.project.log;

import java.lang.reflect.Method;
import java.util.Arrays;

public final class MethodClassKey implements Comparable<MethodClassKey> {
	private final Method method;
	private final Class<?> targetClass;

	public MethodClassKey(Method method, Class<?> targetClass) {
		this.method = method;
		this.targetClass = targetClass;
	}

	public boolean equals(Object other) {
		if (this == other) {
			return true;
		} else if (!(other instanceof MethodClassKey)) {
			return false;
		} else {
			MethodClassKey otherKey = (MethodClassKey) other;
			return this.method.equals(otherKey.method) && this.nullSafeEquals(this.targetClass, otherKey.targetClass);
		}
	}

	public boolean nullSafeEquals(Object o1, Object o2) {
		if (o1 == o2) {
			return true;
		} else if (o1 != null && o2 != null) {
			if (o1.equals(o2)) {
				return true;
			} else {
				return o1.getClass().isArray() && o2.getClass().isArray() ? this.arrayEquals(o1, o2) : false;
			}
		} else {
			return false;
		}
	}

	private boolean arrayEquals(Object o1, Object o2) {
		if (o1 instanceof Object[] && o2 instanceof Object[]) {
			return Arrays.equals((Object[]) ((Object[]) o1), (Object[]) ((Object[]) o2));
		} else if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
			return Arrays.equals((boolean[]) ((boolean[]) o1), (boolean[]) ((boolean[]) o2));
		} else if (o1 instanceof byte[] && o2 instanceof byte[]) {
			return Arrays.equals((byte[]) ((byte[]) o1), (byte[]) ((byte[]) o2));
		} else if (o1 instanceof char[] && o2 instanceof char[]) {
			return Arrays.equals((char[]) ((char[]) o1), (char[]) ((char[]) o2));
		} else if (o1 instanceof double[] && o2 instanceof double[]) {
			return Arrays.equals((double[]) ((double[]) o1), (double[]) ((double[]) o2));
		} else if (o1 instanceof float[] && o2 instanceof float[]) {
			return Arrays.equals((float[]) ((float[]) o1), (float[]) ((float[]) o2));
		} else if (o1 instanceof int[] && o2 instanceof int[]) {
			return Arrays.equals((int[]) ((int[]) o1), (int[]) ((int[]) o2));
		} else if (o1 instanceof long[] && o2 instanceof long[]) {
			return Arrays.equals((long[]) ((long[]) o1), (long[]) ((long[]) o2));
		} else {
			return o1 instanceof short[] && o2 instanceof short[] ? Arrays.equals((short[]) ((short[]) o1), (short[]) ((short[]) o2)) : false;
		}
	}

	public int hashCode() {
		return this.method.hashCode() + (this.targetClass != null ? this.targetClass.hashCode() * 29 : 0);
	}

	public String toString() {
		return this.method + (this.targetClass != null ? " on " + this.targetClass : "");
	}

	public int compareTo(MethodClassKey other) {
		int result = this.method.getName().compareTo(other.method.getName());
		if (result == 0) {
			result = this.method.toString().compareTo(other.method.toString());
			if (result == 0 && this.targetClass != null && other.targetClass != null) {
				result = this.targetClass.getName().compareTo(other.targetClass.getName());
			}
		}

		return result;
	}
}
